package com.yss.adapter.template;

import com.yss.adapter.AdapterExtraInfo;
import com.yss.adapter.IAdapter;
import com.yss.exception.RemotingException;
import com.yss.exception.RemotingTimeOutException;
import com.yss.netty.NettyRemotingClient;
import com.yss.netty.config.NettyClientConfig;
import com.yss.netty.future.ResponseCallback;
import com.yss.netty.protocol.CommandType;
import com.yss.netty.protocol.Message;
import com.yss.netty.protocol.SerType;
import com.yss.netty.util.Constants;
import com.yss.netty.util.Host;
import com.yss.netty.util.serialize.ISerialize;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class INettyAdapterTemplate extends IAdapter {

    private final static NettyClientConfig nettyClientConfig = new NettyClientConfig();

    private final static NettyRemotingClient remotingClient = new NettyRemotingClient(nettyClientConfig);

    static {
        // Start client
        remotingClient.start();
    }

    @Override
    public Object handle(AdapterExtraInfo adapterExtraInfo) throws RemotingException, RemotingTimeOutException {
        return sendByNetty(adapterExtraInfo);
    }

    @Override
    public Object sendByNetty(AdapterExtraInfo adapterExtraInfo) throws RemotingException, RemotingTimeOutException {
        INettyAdapterInfo info = (INettyAdapterInfo) adapterExtraInfo;

        Message response = this.sendMsg(info);
        if (!info.isAsync() && response != null) {
            return this.resolveResponse(response);
        }

        return null;
    }

    /**
     * Only the synchronous method has response
     *
     * @return
     * @throws RemotingException
     * @throws RemotingTimeOutException
     */
    private Message sendMsg(INettyAdapterInfo info) throws RemotingException, RemotingTimeOutException {
        Host host = info.getHost();
        SerType serType = info.getSerType();
        CommandType commandType = info.getCommandType();
        boolean async = info.isAsync();
        Object params = info.getParams();

        ISerialize iSerialize = SerType.matchUtilBySerType(serType);
        ResponseCallback responseCallback = info.getResponseCallback();

        Message message = Message.of(commandType, serType, iSerialize.serialize(params));

        if (async) {
            if (responseCallback == null) {
                throw new RemotingException("if need async send message, you mush give me a callback");
            }
            remotingClient.sendAsync(host, message, Constants.READ_TIME_OUT, responseCallback);
        } else {
            return remotingClient.sendSync(host, message, Constants.READ_TIME_OUT);
        }
        return null;
    }

    /**
     * Resolve the response
     *
     * @param response
     * @return
     */
    private Object resolveResponse(Message response) {
        byte[] body = response.getBody();
        SerType serType = response.getSerType();
        ISerialize iSerialize = SerType.matchUtilBySerType(serType);
        CommandType commandType = response.getCommandType();

        Class mappingClass = commandType.getMappingClass();

        return iSerialize.deserialize(body, mappingClass);
    }

}
